%code requires {
#include "ast.h"
}

%{
#include "ast.h"

#include <cstdio>
#include <iostream>

AstCodeBlock* programBlock;

extern int yylex();
extern int yyparse();
extern FILE *yyin;

void yyerror(const char *s);
%}

%union {
    AstArcExpression* astArcExpression;
    AstBool* astBool;
    AstBoolWithConstraints* astBoolWithConstraints;
    AstCodeBlock* astCodeBlock;
    AstConnection* astConnection;
    AstColsetDeclaration* astColsetDecl;
    AstDeclaration* astDeclaration;
    AstEnum* astEnum;
    AstEnumWithConstraints* astEnumWithConstraints;
    AstIdentifier* astIdentifier;
    AstInt* astInt;
    AstIntWithConstraints* astIntWithConstraints;
    AstMultisetValue* astMultisetValue;
    AstOperator* astOp;
    AstPlace* astPlace;
    AstPlaceParams* astPlaceParams;
    AstStatement* astStatement;
    AstString* astString;
    AstStringWithConstraints* astStringWithConstraints;
    AstTransition* astTransition;
    AstTransitionParams* astTransitionParams;
    AstType* astType;
    AstUnit* astUnit;
    AstUnitWithConstraints* astUnitWithConstraints;
    AstValue* astValue;
    AstValueDeclaration* astValueDecl;
    AstVariableDeclaration* astVariableDecl;
    char* sval;
}

%token <sval> B_VALUE_T I_VALUE_T S_VALUE_T U_VALUE_T
%token <sval> BOOL_T INT_T STRING_T UNIT_T ID_T
%token <sval> CONN_T
%token <sval> COLSET_T DECL_T
%token <sval> ENUMSEP_T VAR_SEP_T SEP_T MULSEP_T
%token <sval> VAL_T VAR_T
%token <sval> RANGE_T WITH_T LAND_T LOR_T COMM_T HYPH_T ARROW_T
%token <sval> LPAREN_T RPAREN_T LSBRA_T RSBRA_T
%token <sval> OEQ_T ONEQ_T OLT_T OGT_T OLTE_T OGTE_T POP_T
%token <sval> PLACE_T NPLACE_T TRANS_T NTRANS_T
%token <sval> STMTDEL_T ERROR_T

%type <astArcExpression> arcExpr;
%type <astBoolWithConstraints> boolColset;
%type <astCodeBlock> stmts;
%type <astConnection> cBody;
%type <astDeclaration> dBody;
%type <astEnumWithConstraints> enumValue;
%type <astIntWithConstraints> intColset;
%type <astMultisetValue> boolMS intMS stringMS unitMS idMS;
%type <astOp> op;
%type <astPlace> place;
%type <astPlaceParams> placeParams;
%type <astStatement> stmt;
%type <astStringWithConstraints> stringColset;
%type <astTransition> transition;
%type <astTransitionParams> tranParams;
%type <astType> type colsetType;
%type <astValue> value multiset;
%type <astUnitWithConstraints> unitColset;

%start program

%%
program:
stmts {programBlock = $1;};
stmts:
stmt STMTDEL_T {$$ = new AstCodeBlock(); $$->addStatement($<astStatement>1);} |
error STMTDEL_T {$$ = new AstCodeBlock();} |
stmts stmt STMTDEL_T {$1->addStatement($<astStatement>2);} |
stmts error STMTDEL_T {};
stmt:
DECL_T dBody {$$ = $2;} |
CONN_T cBody {$$ = $2;};

dBody:
VAR_T ID_T VAR_SEP_T type {$$ = new AstVariableDeclaration(
    new AstIdentifier($2), $<astType>4);
    if (!SymbolTable::i()->addSymbol($2, $$)) { YYERROR; }} |
VAL_T ID_T SEP_T value {$$ = new AstValueDeclaration(
    new AstIdentifier($2), $<astValue>4);
    if (!SymbolTable::i()->addSymbol($2, $$)) { YYERROR; }} |
COLSET_T ID_T SEP_T colsetType {$$ = new AstColsetDeclaration(
    new AstIdentifier($2), $<astType>4);
    if (!SymbolTable::i()->addSymbol($2, $$)) { YYERROR; }};
type:
BOOL_T {$$ = new AstBool();} |
INT_T {$$ = new AstInt();} |
STRING_T {$$ = new AstString();} |
UNIT_T {$$ = new AstUnit();} |
ID_T {$$ = new AstEnum();};
value:
B_VALUE_T {$$ = new AstValue($1);} |
I_VALUE_T {$$ = new AstValue($1);} |
S_VALUE_T {$$ = new AstValue($1);} |
ID_T {$$ = new AstValue($1);} |
multiset {$$ = $1;};
multiset:
boolMS {$$ = $1;} |
intMS {$$ = $1;} |
stringMS {$$ = $1;} |
unitMS {$$ = $1;} |
idMS {$$ = $1;};
boolMS:
I_VALUE_T MULSEP_T B_VALUE_T
{$$ = new AstMultisetValue(); $$->addValue($1, $3);} |
boolMS POP_T I_VALUE_T MULSEP_T B_VALUE_T {$1->addValue($3, $5);};
intMS:
I_VALUE_T MULSEP_T I_VALUE_T
{$$ = new AstMultisetValue(); $$->addValue($1, $3);} |
intMS POP_T I_VALUE_T MULSEP_T I_VALUE_T {$1->addValue($3, $5);};
stringMS:
I_VALUE_T MULSEP_T S_VALUE_T
{$$ = new AstMultisetValue(); $$->addValue($1, $3);} |
stringMS POP_T I_VALUE_T MULSEP_T S_VALUE_T {$1->addValue($3, $5);};
unitMS:
I_VALUE_T MULSEP_T U_VALUE_T
{$$ = new AstMultisetValue(); $$->addValue($1, $3);} |
unitMS POP_T I_VALUE_T MULSEP_T U_VALUE_T {$1->addValue($3, $5);};
idMS:
I_VALUE_T MULSEP_T ID_T
{$$ = new AstMultisetValue(); $$->addValue($1, $3);} |
idMS POP_T I_VALUE_T MULSEP_T ID_T {$1->addValue($3, $5);};
colsetType:
boolColset {$$ = $1;} |
intColset {$$ = $1;} |
stringColset {$$ = $1;} |
unitColset {$$ = $1;} |
WITH_T enumValue {$$ = $2;};
boolColset:
BOOL_T {$$ = new AstBoolWithConstraints();} |
BOOL_T WITH_T LPAREN_T ID_T COMM_T ID_T RPAREN_T
    {$$ = new AstBoolWithConstraints();
        $$->addConstraint($4);
        $$->addConstraint($6);};
intColset:
INT_T {$$ = new AstIntWithConstraints();} |
INT_T WITH_T I_VALUE_T RANGE_T I_VALUE_T
    {$$ = new AstIntWithConstraints();
        $$->addConstraint($3);
        $$->addConstraint($5);};
stringColset:
STRING_T {$$ = new AstStringWithConstraints();} |
STRING_T WITH_T S_VALUE_T RANGE_T S_VALUE_T
    {$$ = new AstStringWithConstraints();
        $$->addConstraint($3);
        $$->addConstraint($5);} |
STRING_T WITH_T S_VALUE_T RANGE_T S_VALUE_T LAND_T I_VALUE_T RANGE_T I_VALUE_T
    {$$ = new AstStringWithConstraints();
        $$->addConstraint($3);
        $$->addConstraint($5);
        $$->addConstraint($7);
        $$->addConstraint($9);};
unitColset:
UNIT_T {$$ = new AstUnitWithConstraints();} |
UNIT_T WITH_T ID_T {$$ = new AstUnitWithConstraints();
    $$->addConstraint($3);};
enumValue:
ID_T {$$ = new AstEnumWithConstraints(); $$->addConstraint($1);} |
enumValue ENUMSEP_T ID_T {$$->addConstraint($3);};

cBody: place HYPH_T arcExpr ARROW_T transition {$$ = new AstConnection(
$<astPlace>1, $<astArcExpression>3, $<astTransition>5);} |
transition HYPH_T arcExpr ARROW_T place {$$ = new AstConnection(
$<astTransition>1, $<astArcExpression>3, $<astPlace>5);};
place: NPLACE_T LPAREN_T ID_T RPAREN_T LSBRA_T placeParams RSBRA_T
{$$ = new AstPlace($3, $<astPlaceParams>6, true);} |
PLACE_T LPAREN_T ID_T RPAREN_T LSBRA_T placeParams RSBRA_T
{$$ = new AstPlace($3, $<astPlaceParams>6, false);};
arcExpr: LSBRA_T multiset RSBRA_T {$$ = new AstArcExpression($<astValue>2);};
transition: NTRANS_T LPAREN_T ID_T RPAREN_T LSBRA_T tranParams RSBRA_T
{$$ = new AstTransition($3, $<astTransitionParams>6, true);}|
TRANS_T LPAREN_T ID_T RPAREN_T LSBRA_T tranParams RSBRA_T
{$$ = new AstTransition($3, $<astTransitionParams>6, false);};
placeParams: type {$$ = new AstPlaceParams($<astType>1);} |
type COMM_T ID_T {$$ = new AstPlaceParams($<astType>1, $3);} |
type COMM_T ID_T COMM_T multiset
{$$ = new AstPlaceParams($<astType>1, $3, $<astValue>5);};
tranParams: %empty {$$ = new AstTransitionParams("", 0, "");} |
ID_T op ID_T {$$ = new AstTransitionParams($1, $<astOp>2, $3);} |
ID_T op multiset
{$$ = new AstTransitionParams($1, $<astOp>2, $<astValue>3);};
op: OEQ_T {$$ = new AstOperator(OperatorEqual);} |
ONEQ_T {$$ = new AstOperator(OperatorNotEqual);} |
OLT_T {$$ = new AstOperator(OperatorLesserThan);} |
OGT_T {$$ = new AstOperator(OperatorGreaterThan);} |
OLTE_T {$$ = new AstOperator(OperatorLesserThanEqual);} |
OGTE_T {$$ = new AstOperator(OperatorGreaterThanEqual);};
%%

AstCodeBlock* compile(const char* fileName) {
    AstCodeBlock* ret = 0;

    FILE *myfile = fopen(fileName, "r");

    if (myfile != 0) {
        yyin = myfile;

        do {
            yyparse();
        } while (!feof(yyin));

        ret = programBlock;
    }

    return ret;
}

void yyerror(const char *s) {
    AstInfo::i()->astHandleError(s);
}
